package com.kamistoat.meimeistore.commonsecurity.utils;

import com.kamistoat.meimeistore.apisystem.model.LoginUser;
import com.kamistoat.meimeistore.commoncore.constant.SecurityConstants;
import com.kamistoat.meimeistore.commoncore.constant.TokenConstants;
import com.kamistoat.meimeistore.commoncore.context.SecurityContextHolder;
import com.kamistoat.meimeistore.commoncore.utils.ServletUtils;
import com.kamistoat.meimeistore.commoncore.utils.StringUtils;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * 权限获取工具类。
 * <p>
 * 其实就是将 SecurityContextHolder 中的方法在抽象了一层而已。
 * 非常的简单，甚至不写这个类，到时候直接用 SecurityContextHolder也是可以的。
 * 此类中的getJwtToken的解释去看网关中的AuthFiler
 */
public class SecurityUtils {
    /**
     * 从 ThreadLocal 中获取 userId
     * @return
     */
    public static Long getUserId() {
        return SecurityContextHolder.getUserId();
    }

    /**
     * 从 ThreadLocal 中获取 userName
     */
    public static String getUserName() {
        return SecurityContextHolder.getUserName();
    }

    /**
     * 从ThreadLocal中获取用户key
     */
    public static String getUserKey() {
        return SecurityContextHolder.getUserKey();
    }

    /**
     * 从ThreadLocal中获取登录用户信息。
     * 用户信息将以 login_user为key存放在 ThreadLocal中
     */
    public static LoginUser getLoginUser() {
        return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class);
    }

    /**
     * 从ThreadLocal中获取用户的额外参数表
     */
    public static Map<String, Object> getUserParams() {
        return getLoginUser().getUserParams();
    }

    /**
     * 获取当前Request请求中的Jwt数据. Jwt是简易用户令牌，含有完整用户令牌的token/userId/userName。
     * 这里用 token做方法名是因为 Jwt含有的token是其唯一标识，并不是指该方法返回jwt的token字段。
     */
    public static String getJwtToken() {
        // 传入当前的 Request
        return getJwtToken(ServletUtils.getRequest());
    }

    /**
     * 获取指定Request请求中的Jwt数据. Jwt是简易用户令牌，含有完整用户令牌的token/userId/userName
     * 这里用 token做方法名是因为 Jwt含有的token是其唯一标识，并不是指该方法返回 token。
     */
    public static String getJwtToken(HttpServletRequest httpServletRequest) {
        // 从输入 Request的请求头中获取 key为 "Authorization" 的简易用户令牌信息
        String jwtToken = httpServletRequest.getHeader(TokenConstants.AUTHENTICATION);
        return replaceTokenPrefix(jwtToken);
    }

    /**
     * 裁剪 简易用户令牌信息 前缀。
     * 这里的输入输出 token 同样是代表用户令牌信息，不是令牌内的token字段
     *
     * @param jwtToken 未经裁剪的 简易用户令牌
     * @return 裁剪后的 简易用户令牌
     */
    public static String replaceTokenPrefix(String jwtToken) {
        // 因为传到后端的 简易用户令牌信息 到底带不带前缀是由前端代码决定的。
        // 但是这个前缀是啥是还是需要前后端保持相同的，这里我们设置为国际通用的 Bearer+空格+简易令牌信息
        // 检测是否存在令牌前缀，存在则裁剪掉前缀
        if (StringUtils.isNotEmpty(jwtToken) && jwtToken.startsWith(TokenConstants.PREFIX)) {
            jwtToken = jwtToken.replaceFirst(TokenConstants.PREFIX, "");
        }
        return jwtToken;
    }

    /**
     * 判断当前ThreadLocal用户是否为超级管理员
     * @return
     */
    public static boolean curLoginUserIsAdmin(){
        LoginUser loginUser = getLoginUser();
        return loginUser.getWeatherAdmin();
    }
//
//    /**
//     * 是否为管理员.
//     * <p>
//     * P.S. 这里还是有点问题的，原项目这里是直接认为只要userId为1L这就是管理员。正确的做法应该是在根据其userId去查其拥有的roleId
//     *
//     * @param userId 用户ID
//     * @return 结果
//     */
//    public static boolean isAdmin(Long userId) {
//        return userId != null && userId == 1L;
//    }

    /**
     * 生成BCryptPasswordEncoder密码
     *
     * @param password 密码
     * @return 加密字符串
     */
    public static String encryptPassword(String password) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.encode(password);
    }

    /**
     * 判断密码是否相同
     *
     * @param rawPassword     一般是前端传递的未经加密的明文密码
     * @param encodedPassword 一般是从数据库中查询出的加密后的密码
     * @return 结果
     */
    public static boolean matchesPassword(String rawPassword, String encodedPassword) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }
}
